#include "fxcc/core/graph/opengl3/ImGuiGlfwApp.h"
#include "fxcc/core/graph/common/Geometry.h"
#include "fxcc/imgui/Editor.h"

using namespace fxcc::platform;
using namespace fxcc::graph;
using namespace fxcc::graph::opengl3;
using namespace fxcc::imgui::graph::opengl3;
using BaseApp = fxcc::imgui::graph::opengl3::GlfwApp;

#undef main

struct Object
{
    fxcc::graph::common::Transform transform;
    glm::vec4 m_Color{};
};

struct SimpleApp : public BaseApp
{
public:
    fxcc::graph::common::Transform transform;

    glm::vec4 diffuseColor{ 0.5f,1.0f,0.5f,1.f };
    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_RenderTarget;
    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_ThresholdRT;
    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_GassRTHorizontal;
    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_GassRTVetical;
    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_BloomRT;
    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_OutRT;

    std::shared_ptr<fxcc::graph::opengl3::Mesh> m_CubeMesh;


    glm::vec4 color;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_Effect;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_TresholdEffect;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_GassEffect;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_BloomEffect;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_OutEffect;


    std::vector<Object> m_objects;
    const int numObjs = 25;

    fxcc::graph::common::Camera camera;

    // threshold
    float threshold = 0.5f;

    // Gass
    bool m_IsHorizontal{ 0 };
    int m_BlurRadius = 20;

    // bloom
    float lambda{ 0.5f };
    int bloomMipLevels{ 5 };
    int no{ 0 };

    // ��֪��զ��Ƶ� ���Ǻ����� ��hdr�Ǹ������Զ
    // Out
    float bloomStrength{ 2.0f };

    SimpleApp(const fxcc::platform::common::App::Desc &desc) : BaseApp(desc), color(glm::vec4(1.0f))
    {
    }
    bool Init() override
    {
        if (!BaseApp::Init())
        {
            return false;
        }

        if (!InitResources())
        {
            return false;
        }

        fxcc::graph::common::Random random;

        m_objects.resize(numObjs);
        std::vector<fxcc::graph::common::Transform> transforms = random.GenerateTransforms(numObjs);

        std::vector<glm::vec4> colors;
        colors.resize(numObjs);
        random.GetVec4(&colors[0], numObjs, { 0.0f,0.0f,0.0f ,0.0f }, { 1.0f,1.0f,1.0f ,1.0f });

        for (int i = 0; i < numObjs; i++)
        {
            m_objects[i].transform = transforms[i];
            m_objects[i].m_Color = glm::vec4(colors[i]);
        }

        //quadTransform.m_Position = glm::vec3(-m_Desc.m_Size.x / 2.0f, -m_Desc.m_Size.y / 2.f, 1.f);
        //quadTransform.m_Position = glm::vec3(m_Desc.m_Size.x / 2.0f, m_Desc.m_Size.y / 2.0f, 0.f);

        return true;
    };

    void printOpenGLESVersion()
    {
        const char *version = (const char *)glGetString(GL_VERSION);
        std::cout << "OpenGL ES Version: " << version << std::endl;
    };

    // renderTarget use the scrWidth scrHeight
    bool InitResources()
    {

        camera.SetPosition(glm::vec3(25.0f));
        camera.LookAt(glm::vec3(0.0f));

        const std::string fragCode = R"(

out vec4 FragColor;

uniform vec4 diffuseColor;

void main()
{
    FragColor=diffuseColor;
}
)";
        m_Effect = std::make_shared<fxcc::graph::opengl3::Effect3d>(fragCode);

        const std::string thresholdFragCode = R"(

out vec4 FragColor;

uniform float threshold; 
uniform sampler2D sceneTexture;

void main() {
    vec3 color = texture(sceneTexture, TexCoords).rgb;

    float brightness = max(color.r, max(color.g, color.b));

    if (brightness > threshold) {
        FragColor = vec4(color, 1.0);  
    } else {
        discard;
    }
}
)";
        m_TresholdEffect = std::make_shared<fxcc::graph::opengl3::Effect3d>(thresholdFragCode);
        m_TresholdEffect->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;

        const std::string gassFragCode = R"(

out vec4 FragColor;

uniform sampler2D sceneTexture;

uniform vec2 texelSize; 
uniform bool isHorizontal;
uniform int blurRadius ; 

void main()
{
    vec3 result = texture(sceneTexture, TexCoords).rgb;


    if (isHorizontal) {
        for (int i = 1; i <= blurRadius; ++i) {
            result += texture(sceneTexture, TexCoords + vec2(i * texelSize.x, 0.0)).rgb;
            result += texture(sceneTexture, TexCoords - vec2(i * texelSize.x, 0.0)).rgb;
        }
    } else {
        for (int i = 1; i <= blurRadius; ++i) {
            result += texture(sceneTexture, TexCoords + vec2(0.0, i * texelSize.y)).rgb;
            result += texture(sceneTexture, TexCoords - vec2(0.0, i * texelSize.y)).rgb;
        }
    }

    FragColor = vec4(result / (2.0 * blurRadius + 1.0), 1.0);
}
)";
        m_GassEffect = std::make_shared<opengl3::Effect3d>(gassFragCode);
        m_GassEffect->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;

        const std::string bloomFragCode = R"(

out vec4 FragColor;

uniform sampler2D gassH;  
uniform sampler2D gassV;
uniform float lambda;
uniform int bloomMipLevels;

void main()
{
    vec3 bloomColor = vec3(0.0);
    
    float totalWeight = (1.0 - exp(-lambda * bloomMipLevels)) / (1.0 - exp(-lambda));

    for (int i = 0; i < bloomMipLevels; ++i) {
        float weight = exp(-lambda * i) / totalWeight; 
        
        vec3 hBlur = textureLod(gassH, TexCoords, i).rgb;
        vec3 vBlur = textureLod(gassV, TexCoords, i).rgb;
        
        bloomColor += (hBlur + vBlur) * 0.5 * weight;
    }

    FragColor = vec4(bloomColor, 1.0);
}
)";
        m_BloomEffect = std::make_shared<fxcc::graph::opengl3::Effect3d>(bloomFragCode);
        m_BloomEffect->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;

        const std::string outFragCode = R"(

out vec4 FragColor;
uniform sampler2D sceneTexture;
uniform sampler2D bloomTexture;
uniform float bloomStrength;

void main()
{
    vec3 sceneColor = texture(sceneTexture, TexCoords).rgb;

    vec2 bloomTexCoord=TexCoords;
    bloomTexCoord.y= 1- bloomTexCoord.y;
    vec3 bloomColor = texture(bloomTexture, bloomTexCoord).rgb;
    FragColor = vec4(sceneColor + bloomColor * bloomStrength, 1.0);
}
)";
        m_OutEffect = std::make_shared<fxcc::graph::opengl3::Effect3d>(outFragCode);
        m_OutEffect->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;

        auto geoData = fxcc::graph::common::Geometry::CreateBox();
        fxcc::graph::common::MeshDesc meshDesc(geoData);
        m_CubeMesh = std::make_shared<fxcc::graph::opengl3::Mesh>(meshDesc);

        return true;
    };

    fxcc::imgui::Editor editor;

    void OnEdit() override
    {
        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

        ImGui::Begin("Scene");
        editor.AddFrameBuffer(m_RenderTarget->m_Textures[0]->GetTextureId());
        ImGui::End();

        ImGui::Begin("Threshold");
        editor.AddFrameBuffer(m_ThresholdRT->m_Textures[0]->GetTextureId(), { 0, 0 }, { 1, 1 });
        ImGui::End();

        ImGui::Begin("Gass Horizontal");
        editor.AddFrameBuffer(m_GassRTHorizontal->m_Textures[0]->GetTextureId(), { 0, 0 }, { 1, 1 });
        ImGui::End();

        ImGui::Begin("Gass Vertical");
        editor.AddFrameBuffer(m_GassRTVetical->m_Textures[0]->GetTextureId(), { 0, 0 }, { 1, 1 });
        ImGui::End();

        ImGui::Begin("bloom Texture");
        editor.AddFrameBuffer(m_BloomRT->m_Textures[0]->GetTextureId(), { 0, 0 }, { 1, 1 });
        ImGui::End();

        ImGui::Begin("out Texture");
        editor.AddFrameBuffer(m_OutRT->m_Textures[0]->GetTextureId(), { 0, 0 }, { 1, 1 });
        ImGui::End();

        ImGui::Begin("threshold");
        ImGui::DragFloat("threshold", &threshold, 0.1f, 0.0f, 1.0f);
        ImGui::End();
    
        ImGui::Begin("bloom Config");

        ImGui::DragFloat("lambda", &lambda, 0.1f, 0.7, 1.2f);
        ImGui::DragInt("bloomMipLevels", &bloomMipLevels, 0.1f, 0, 20);
        ImGui::DragInt("no", &no, 0.1f, 0, 20);
        ImGui::End();

        ImGui::Begin("Out");
        ImGui::DragFloat("bloomStrength", &bloomStrength, 0.1f, 0.0f);
        ImGui::End();

        ImGui::Begin("Objects");

        for (auto& o : m_objects)
        {
            ImGui::PushID(&o);
            editor.ColorInst<glm::vec4>("diffuseColor", o.m_Color);
            editor.EditInst<fxcc::graph::common::Transform>("transform", o.transform);

            ImGui::PopID();
        }
        ImGui::End();

        ImGui::Begin("Gass Config");
        editor.CheckBoxInst<bool>("isHorizonal", m_IsHorizontal);
        editor.InputInst<int>("radius", m_BlurRadius);
        ImGui::End();
    }
    void OnDestroy() override
    {
      
        BaseApp::OnDestroy();
    }
    void OnFrameRender() override
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        m_RenderTarget->Begin();

        m_Effect->Begin();
        m_Effect->ClearBgColor(glm::vec4(0.f,0.f,0.f,1.f));
        m_Effect->ClearBuffer(1, 1);
        m_Effect->SetCamera(camera);
        m_Effect->m_Shader->Use();

        for (auto o : m_objects)
        {
            m_Effect->m_Shader->setVec4("diffuseColor", o.m_Color);
            m_Effect->InstMesh(m_CubeMesh.get(), o.transform);

        }
        m_Effect->End();
        m_RenderTarget->End();

        
        m_ThresholdRT->Begin();
        m_TresholdEffect->Begin();
        m_TresholdEffect->m_Shader->use();
        m_TresholdEffect->m_Shader->setFloat("threshold", threshold);
        m_TresholdEffect->DebugTexture2D(m_RenderTarget->m_Textures[0].get(), 0, "sceneTexture");
        m_TresholdEffect->End();
        m_ThresholdRT->End();

        // ���в���z
        m_GassRTHorizontal->Begin();
        m_GassEffect->Begin();
        m_GassEffect->m_Shader->Use();
        m_GassEffect->m_Shader->setVec2("texelSize", glm::vec2(1.f / m_Desc.m_Size.x, 1.f / m_Desc.m_Size.x));
        m_GassEffect->m_Shader->setBool("isHorizontal", true);
        m_GassEffect->m_Shader->setInt("blurRadius", m_BlurRadius);
        m_GassEffect->DebugTexture2D(m_ThresholdRT->m_Textures[0].get(), 0, "sceneTexture");
        m_GassEffect->End();
        m_GassRTHorizontal->End();

    
        m_GassRTVetical->Begin();
        m_GassEffect->Begin();
        m_GassEffect->m_Shader->Use();
        m_GassEffect->m_Shader->setVec2("texelSize", glm::vec2(1.f / m_Desc.m_Size.x, 1.f / m_Desc.m_Size.x));
        m_GassEffect->m_Shader->setBool("isHorizontal", false);
        m_GassEffect->m_Shader->setInt("blurRadius", m_BlurRadius);
        m_GassEffect->DebugTexture2D(m_ThresholdRT->m_Textures[0].get(), 0, "sceneTexture");
        m_GassEffect->End();
        m_GassRTVetical->End();

        m_GassRTHorizontal->m_Textures[0]->Bind();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        m_GassRTHorizontal->m_Textures[0]->Mipmap();

        m_GassRTVetical->m_Textures[0]->Bind();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        m_GassRTVetical->m_Textures[0]->Mipmap();


        m_BloomRT->Begin();
        m_BloomEffect->Begin();

        auto texture = m_ThresholdRT->m_Textures[0].get();
        m_BloomEffect->SetTexture2DPassData(texture);
        m_BloomEffect->SetTexture2D(m_GassRTHorizontal->m_Textures[0].get(), 0, "gassH");
        m_BloomEffect->SetTexture2D(m_GassRTVetical->m_Textures[0].get(), 1, "gassV");
 
        m_BloomEffect->m_Shader->Use();
        m_BloomEffect->m_Shader->setInt("bloomMipLevels", bloomMipLevels);
        m_BloomEffect->m_Shader->setInt("no", no);
        m_BloomEffect->m_Shader->setFloat("lambda", lambda);
        m_BloomEffect->InstQuad();
        m_BloomEffect->End();
        m_BloomRT->End();

        m_OutRT->Begin();
        m_OutEffect->Begin();
        m_OutEffect->SetTexture2DPassData(texture);
        m_OutEffect->SetTexture2D(m_RenderTarget->m_Textures[0].get(), 0, "sceneTexture");
        m_OutEffect->SetTexture2D(m_BloomRT->m_Textures[0].get(), 1, "bloomTexture");
        m_OutEffect->m_Shader->Use();
        m_OutEffect->m_Shader->setFloat("bloomStrength", bloomStrength);
        m_OutEffect->InstQuad();

        m_OutEffect->End();
        m_OutRT->End();
    };

    void OnFramebuffersize(int w, int h) override
    {

        opengl3::GlfwApp::OnFramebuffersize(w, h);
        camera.SetAspect(m_Desc.Aspect());

        // Debug Quad Config
        {
        	fxcc::graph::common::RenderTargetDesc desc;
            desc.InitDesc2(1, m_Desc.m_Size.x, m_Desc.m_Size.y, 4, 1);

            m_RenderTarget = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);
            m_ThresholdRT = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);
            m_GassRTHorizontal = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);
            m_GassRTVetical = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);
            m_BloomRT = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);
            m_OutRT = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);

        }
    }
    void OnUpdate() override
    {
        opengl3::GlfwApp::OnUpdate();

        float ctTime = m_GlfwlTimer.m_CurrentTime;
        if (m_IsHorizontal)
        {
            camera.FreeMove(m_Input, m_GlfwlTimer.m_DeltaTime);

        }

        float deltaTime = m_GlfwlTimer.m_DeltaTime;


    }
};

int main()
{
    ztclog::pushFunction([](const ztclog::Line &line)
                         {
		if (line.m_Type != ztclog::Line::Type::_line_debug_)
		{
			std::cout << line.m_Text << std::endl;
		} });

    using _App = SimpleApp;
    using _Desc = fxcc::platform::common::App::Desc;

    _Desc desc;
    desc.m_Pos = glm::ivec2(100, 100);
    desc.m_Size = glm::ivec2(900, 600);
    desc.m_ScaleFactor = 1.0f;

    desc.m_Title = "test font";
    std::shared_ptr<_App> _app = std::make_shared<_App>(desc);
    return _app->Run();
};
